package com.yuandian.business.monitor.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuandian.api.business_monitor.po.QueryRetStatisInfoParams;
import com.yuandian.api.business_monitor.po.SegmentOverviewParams;
import com.yuandian.api.business_monitor.vo.*;
import com.yuandian.api.management.entity.ConfNodeProperty;
import com.yuandian.api.management.enums.GraphType;
import com.yuandian.api.management.feign.RemoteItRoadMapService;
import com.yuandian.api.management.feign.RemoteNodePropertyService;
import com.yuandian.base.service.ITaskDataService;
import com.yuandian.bpm.common.core.constant.CommonConstants;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.business.monitor.mapper.SegmentOverviewMapper;
import com.yuandian.business.monitor.service.ISegmentOverviewService;
import com.yuandian.constant.Constants;
import com.yuandian.enums.IndicatorTypeEnum;
import com.yuandian.enums.ScaleTypeEnum;
import com.yuandian.po.BaseSearch;
import com.yuandian.utils.PageDataInfo;
import com.yuandian.utils.PartitionUtil;
import com.yuandian.utils.StringUtils;
import com.yuandian.utils.YDateUtil;
import com.yuandian.vo.TrendDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 说明：网端总览service实现
 *
 * @Author yanjun
 * @Date 2020/10/12 13:59
 */
@Slf4j
@Service
public class SegmentOverviewServiceImpl implements ISegmentOverviewService {

	@Autowired
	private SegmentOverviewMapper segmentOverviewMapper;

	@Autowired
	private RemoteNodePropertyService remoteNodePropertyService;

	@Autowired
	private ITaskDataService taskDataService;

	@Autowired
	private RemoteItRoadMapService remoteItRoadMapService;

	/**
	 * 查询网端总览路径图
	 *
	 * @param params 网端总览查询对象
	 * @return 通用返回对象
	 */
	@Override
	public List<Map<String, Object>> queryOverviewRoad(SegmentOverviewParams params) {
		SegmentOverviewRoadVo sorVo = new SegmentOverviewRoadVo();

		// 1、获取网端总览每个端的位置信息映射关系
		ConfNodeProperty confNodeProperty = new ConfNodeProperty();
		confNodeProperty.setType(GraphType.SEGMENT_MONITOR_DETAIL_IT_ROAD.getType());
		confNodeProperty.setSystemId(params.getSystemId());
		confNodeProperty.setGraphId(String.valueOf(params.getSystemId()));
		log.info("1、开始调用远程服务获取网端监控详情-业务步骤IT路径图结构数据");
		R<List<Map<String, Object>>> listR = remoteItRoadMapService.segmentMonitorDetailItRoadGraph(confNodeProperty);
		int code = listR.getCode();
		if (code == CommonConstants.FAIL) {
			log.error("调用远程服务获取网端监控详情-业务步骤IT路径图结构数据失败，错误：{}", listR.getMsg());
		}
		List<Map<String, Object>> segmentList = listR.getData();
		log.info("调用远程服务获取网端监控详情-业务步骤IT路径图结构数据成功！");

		// 2、查询网端总览
		List<SegmentIndicatorTrendVo> sitVoList = segmentOverviewMapper.querySegmentOverviewRoad(params);
		Map<Integer, SegmentIndicatorTrendVo> dataCollect = sitVoList
				.stream()
				.collect(Collectors.toMap(SegmentIndicatorTrendVo::getSegmentId, vo -> vo, (k1, k2) -> k2));
		log.info("3、查询网端监控详情数据结束，开始组装数据");
		// 3、组装vo:bpm只填充数据内容，不改变数据结构
		mergeData(segmentList, dataCollect);
		return segmentList;
	}
	/**
	 *  合并数据
	 * @param segmentList 网端结构数据
	 * @param dataCollect 网端总览详情数据
	 */
	public void mergeData(List<Map<String, Object>> segmentList, Map<Integer, SegmentIndicatorTrendVo> dataCollect) {
		for (Map<String, Object> map : segmentList) {
			SegmentIndicatorTrendVo segmentIndicator = dataCollect.get(map.get("segmentId"));
			map.put("reqCount", 0);//请求量
			map.put("failReqCount", 0);//失败请求量
			map.put("alarmReqCount", 0);//告警请求量
			map.put("avgDealTime", 0);//平均处理时长
			map.put("health", 0.00f);//健康度
			map.put("totalBytes", 0L);//流量
			if (ObjectUtil.isNotEmpty(segmentIndicator)) {//网端指标数据为空
				map.put("reqCount", segmentIndicator.getReqCount());//请求量
				map.put("failReqCount", segmentIndicator.getFailReqCount());//失败请求量
				map.put("alarmReqCount", segmentIndicator.getAlarmReqCount());//告警请求量
				map.put("avgDealTime", segmentIndicator.getAvgDealTime());//平均处理时长
				map.put("health", segmentIndicator.getHealth());//健康度
				map.put("totalBytes", segmentIndicator.getTotalBytes());//流量

			}
			// 判断网端请求量是否为空,1则不为空，0则为空
			if (ObjectUtil.isNotEmpty(map.get("reqCount")) && (int) map.get("reqCount") > 0) {
				map.put("reqSumIsZero", 1);
			} else {
				map.put("reqSumIsZero", 0);
			}
			List<Map<String, Object>> childNodes = (List<Map<String, Object>>) map.get("childNodes");
			if(ObjectUtil.isNotEmpty(childNodes)){
				mergeData(childNodes, dataCollect);
			}

		}
	}


	/**
	 * 查询网端指标趋势
	 *
	 * @param params 网端总览查询对象
	 * @return 通用返回对象
	 */
	@Override
	public TrendDataVo queryIndicatorTrend(SegmentOverviewParams params) {
		long startTime = System.currentTimeMillis();
		IndicatorTrendShowVo indicatorTrendShowVo = new IndicatorTrendShowVo();
		List<SegmentReqTrend> segmentReqTrends = new ArrayList<>();
		List<RequestTrendVo> reqCountHisVoToday = new ArrayList<>();
		List<RequestTrendVo> reqCountHisVoWeek = new ArrayList<>();
		List<RequestTrendVo> reqCountHisVoMonth = new ArrayList<>();
		String startTimeStr = params.getStartTime();
		String endTimeStr = params.getEndTime();

		// 2、查询网端指标趋势(当前值)
		List<SegmentIndicatorTrendVo> sitVoList = segmentOverviewMapper.querySegmentIndicatorTrend(params);
		indicatorTrendShowVo.setSitVoList(sitVoList);



		// 切割时间段
		List<Map<String, String>> timeList = YDateUtil.timeCutting(params.getStartTime(), params.getEndTime());
		for (Map<String, String> timeMap : timeList) {
			String sTime = timeMap.get("startTime");
			String eTime = timeMap.get("endTime");
			String dateStr = sTime.substring(0, 10);

			// 当天请求量
			params.setIndicatorType(IndicatorTypeEnum.REQ_COUNT.getType());
			params.setStartTime(sTime);
			params.setEndTime(eTime);
			List<RequestTrendVo> childTodayList = segmentOverviewMapper.querySegmentReqTrend(params, 1);
			for (RequestTrendVo todayVo : childTodayList) {
				todayVo.setLabelTime(dateStr + " " + todayVo.getLabelTime());
			}
			reqCountHisVoToday.addAll(childTodayList);

			// 周平均请求量
			String endTime = YDateUtil.format(YDateUtil.parse(eTime, YDateUtil.YYYY_MM_DD_HH_MM_SS), YDateUtil.HH_MM_SS);
			if (!"00:00:00".equals(endTime)) {
				params.setEndTimeStr(endTime);    // 结束时间字符串
			}

			String weekStartTime = YDateUtil.getBeforeDayTime(sTime, 7);
			String weekEndTime = YDateUtil.getBeforeDayTime(eTime, 1);
			params.setStartTime(weekStartTime);
			params.setEndTime(weekEndTime);
			List<RequestTrendVo> childWeekList = segmentOverviewMapper.querySegmentReqTrend(params, 7);    // 请求量 周平均
			// 拼接 日期
			for (RequestTrendVo weekVo : childWeekList) {
				weekVo.setLabelTime(dateStr + " " + weekVo.getLabelTime());
			}
			reqCountHisVoWeek.addAll(childWeekList);

			// 月平均请求量
			String monthStartTime = YDateUtil.getBeforeDayTime(sTime, 30);
			String monthEndTime = YDateUtil.getBeforeDayTime(eTime, 1);
			params.setStartTime(monthStartTime);
			params.setEndTime(monthEndTime);
			List<RequestTrendVo> childMonthList = segmentOverviewMapper.querySegmentReqTrend(params, 30);
			// 拼接 日期
			for (RequestTrendVo monthVo : childMonthList) {
				monthVo.setLabelTime(dateStr + " " + monthVo.getLabelTime());
			}
			reqCountHisVoMonth.addAll(childMonthList);
		}

		// 5. 组装结果集, 进行补0操作
		List<String> scaleTimeList = YDateUtil.getTimeListByJDK8(startTimeStr, endTimeStr, YDateUtil.YYYY_MM_DD_HH_MM_SS, 10);
		Map<String, RequestTrendVo> dayCollectMap = reqCountHisVoToday.stream().collect(Collectors.toMap(RequestTrendVo::getLabelTime, v -> v, (k1, k2) -> k1));
		Map<String, RequestTrendVo> weekCollectMap = reqCountHisVoWeek.stream().collect(Collectors.toMap(RequestTrendVo::getLabelTime, v -> v, (k1, k2) -> k1));
		Map<String, RequestTrendVo> monthCollectMap = reqCountHisVoMonth.stream().collect(Collectors.toMap(RequestTrendVo::getLabelTime, v -> v, (k1, k2) -> k1));

		for (String time : scaleTimeList) {
			RequestTrendVo dayVo =	dayCollectMap.get(time);
			RequestTrendVo weekVo =	weekCollectMap.get(time);
			RequestTrendVo monthVo = monthCollectMap.get(time);

			SegmentReqTrend segmentReqTrend = new SegmentReqTrend();
			segmentReqTrend.setLabelTime(time);
			segmentReqTrend.setIndicator(ObjectUtil.isNotNull(dayVo) ? dayVo.getReqCount() : 0);
			segmentReqTrend.setIndicatorValueWeek(ObjectUtil.isNotNull(weekVo) ? weekVo.getReqCount() : 0);
			segmentReqTrend.setIndicatorValueByteWeek(ObjectUtil.isNotNull(weekVo) ? weekVo.getTotalBytes() : 0);
			segmentReqTrend.setIndicatorValueMonth(ObjectUtil.isNotNull(monthVo) ? monthVo.getReqCount() : 0);
			segmentReqTrend.setIndicatorValueByteMonth(ObjectUtil.isNotNull(monthVo) ? monthVo.getTotalBytes() : 0);

			segmentReqTrends.add(segmentReqTrend);
		}
		indicatorTrendShowVo.setReqCountHisVoList(segmentReqTrends);

		// 查询历史峰值(请求量峰值, 流量峰值)
		BusinessAvgAndPeakVo requestAvgAndPeakVo = new BusinessAvgAndPeakVo();
		BusinessAvgAndPeakVo byteAvgAndPeakVo = new BusinessAvgAndPeakVo();
		params.setStartTime(startTimeStr);
		params.setEndTime(endTimeStr);
		queryPeakValue(requestAvgAndPeakVo, params, "REQUEST");	// 请求量峰值
		queryPeakValue(byteAvgAndPeakVo, params, "BYTE");	// 流量峰值


		// 查询 失败请求量、告警请求量、平均响应时长 历史数据
		startTime = System.currentTimeMillis();
		params.setStartTime(startTimeStr);
		params.setEndTime(endTimeStr);
		List<Integer> IndicatorTypeList = Arrays.asList(
				IndicatorTypeEnum.FAIL_REQ_COUNT.getType(), 	// 失败请求量
				IndicatorTypeEnum.ALARM_REQ_COUNT.getType(),  	// 告警请求量
				IndicatorTypeEnum.AVG_DEAL_TIME.getType()); 	// 平均响应时长

		List<Callable<Map<String, Object>>> tasks = new ArrayList<>();
		for (int timeInterval : IndicatorTypeList) { tasks.add(() -> getDataByIndicatorType(params, timeInterval)); }
		ExecutorService executor = Executors.newFixedThreadPool(tasks.size());

		try {
			List<Future<Map<String, Object>>> futures = executor.invokeAll(tasks);
			for (Future< Map<String, Object>> future : futures) {
				Map<String, Object> map = future.get();
				int intervalType = (int) map.get("intervalType");
				List<IndicatorHistoryVo> interValList = (List<IndicatorHistoryVo>) map.get("interValList");
				if (intervalType == IndicatorTypeEnum.FAIL_REQ_COUNT.getType()) {
					indicatorTrendShowVo.setFailReqCountHisVoList(interValList);
				} else if (intervalType == IndicatorTypeEnum.ALARM_REQ_COUNT.getType()) {
					indicatorTrendShowVo.setAlarmReqCountHisVoList(interValList);
				} else {
					indicatorTrendShowVo.setAvgDealTimeHisVoList(interValList);
				}
			}

			log.info("[BPM模块]-[网端监控详情-查询 失败请求量、告警请求量、平均响应时长 历史数据]-耗时: {} MS", System.currentTimeMillis() - startTime);

		} catch (Exception e) {
			e.printStackTrace();
			log.error("[BPM模块]-[网端监控详情-查询 失败请求量、告警请求量、平均响应时长 历史数据]-报错: {}", e.getMessage());
		} finally {
			executor.shutdown();
		}

		TrendDataVo trendDataVo = taskDataService.getTrendObject("系统网端趋势统计", params.getStartTime(), params.getEndTime(), indicatorTrendShowVo);

		// 网端监控详情的趋势图，是以10分钟为一个刻度，所以趋势图显示时，趋势图的起点也只能从每十分钟开始到结束
		Date trendStartTime = YDateUtil.parseDate(trendDataVo.getTrendStartTime());
		Date trendEndTime = YDateUtil.parseDate(trendDataVo.getTrendEndTime());
		int trendStartTimeMinute = YDateUtil.getMinute(trendStartTime) % 10;
		int trendEndTimeMinute = YDateUtil.getMinute(trendEndTime) % 10;
		if (trendStartTimeMinute != 0) {
			Date trendStartResult = new Date(trendStartTime.getTime() + (10 - trendStartTimeMinute) * 60 * 1000);
			trendDataVo.setTrendStartTime(YDateUtil.parseDateToStr(YDateUtil.YYYY_MM_DD_HH_MM_SS, trendStartResult));
		}
		if (trendEndTimeMinute != 0) {
			Date trendEndResult = new Date(trendEndTime.getTime() - trendEndTimeMinute * 60 * 1000);
			trendDataVo.setTrendEndTime(YDateUtil.parseDateToStr(YDateUtil.YYYY_MM_DD_HH_MM_SS, trendEndResult));
		}

		return trendDataVo;
	}


	/**
	 * 查询不同指标数据
	 * @param params	请求参数
	 * @param timeInterval	指标类型
	 * @return	结果集
	 */
	public Map<String, Object> getDataByIndicatorType(SegmentOverviewParams params, int timeInterval) {
		Map<String, Object> map = new ConcurrentHashMap<>();
		List<IndicatorHistoryVo> indicatorHistoryVos = segmentOverviewMapper.querySegmentIndicatorTrendHis(params, timeInterval);
		map.put("intervalType", timeInterval);
		map.put("interValList", indicatorHistoryVos);
		return map;
	}


	/**
	 * 查询历史峰值
	 * @param avgAndPeakVo 结果对象
	 * @param params    请求参数
	 * @param peakType    峰值类型： REQUEST-请求量峰值  BYTE-流量峰值
	 */
	public void queryPeakValue(BusinessAvgAndPeakVo avgAndPeakVo, SegmentOverviewParams params, String peakType) {
		long startTime = System.currentTimeMillis();
		long peakValue = 0; int scale = 10;
		ExecutorService executor = null;
		String peakStartTime = "", scaleType = ScaleTypeEnum.MINUTE.name();

		try {
			String endTime = YDateUtil.format(new Date(), Constants.YYYY_MM_DD_00_00_00);
			List<BaseSearch> timeIntervalList = PartitionUtil.getTimeIntervalList(endTime, 5, 20);
			List<Callable<BusinessAvgAndPeakVo>> tasks = new ArrayList<>();

			// 查询数据
			for (BaseSearch timeInterval : timeIntervalList) {
				tasks.add(() -> segmentOverviewMapper.queryPeakValue(params, scale, scaleType, peakType, timeInterval));
			}
			executor = Executors.newFixedThreadPool(tasks.size());
			List<Future<BusinessAvgAndPeakVo>> futures = executor.invokeAll(tasks);

			// 封装数据
			for (Future<BusinessAvgAndPeakVo> future : futures) {
				BusinessAvgAndPeakVo peakVo = future.get();
				if (peakVo != null && peakVo.getPeakValue() > peakValue) {
					peakValue = peakVo.getPeakValue();
					peakStartTime = peakVo.getPeakStartTime();
				}
			}

			avgAndPeakVo.setPeakValue(peakValue);
			avgAndPeakVo.setPeakStartTime(peakStartTime);
			if (StringUtils.isNotEmpty(peakStartTime)) {
				avgAndPeakVo.setPeakEndTime(YDateUtil.getAfterMinuteStr(peakStartTime, scale));
			}

			log.info("[BPM模块]-[网端监控详情-获取 " + ("REQUEST".equals(peakType) ? "请求量" : "流量") + " 历史峰值 信息]-耗时: {} MS", System.currentTimeMillis() - startTime);

		} catch (Exception e) {
			e.printStackTrace();
			log.error("[BPM模块]-[网端监控详情-获取 " + ("REQUEST".equals(peakType) ? "请求量" : "流量") + " 历史峰值 信息]-报错: {}", e.getMessage());

		} finally {
			if (executor != null) { executor.shutdown(); }
		}
	}


	/**
	 * 查询返回码统计信息
	 *
	 * @param params 查询返回码统计信息参数
	 * @return 返回码统计信息vo对象集合
	 */
	@Override
	public List<RetStatisInfoVo> queryRetStatisInfo(QueryRetStatisInfoParams params) {
		// 查询返回码统计信息
		return segmentOverviewMapper.queryRetStatisInfo(params);
	}


	/**
	 * 查询网端数据总览
	 *
	 * @param params 分页参数
	 * @return 同一返回对象
	 */
	@Override
	public PageDataInfo querySegmentDataOverview(SegmentOverviewParams params) {

		if (ObjectUtil.isEmpty(params.getOrderByColumn())) {
			params.setOrderByColumn("reqCount");
			params.setIsAsc("desc");
		}
		// 4、执行查询
		IPage<SegmentOverviewVo> page = segmentOverviewMapper.querySegmentDataOverview(new Page<>(params.getPageNum(), params.getPageSize()), params);
		return new PageDataInfo(page.getTotal(), page.getRecords());
	}

	/**
	 * 修改网端坐标
	 *
	 * @param segmentPositionVo 网端坐标vo对象
	 * @return 通用返回对象
	 */
	@Override
	public R updateSegmentPosition(SegmentPositionVo segmentPositionVo) {
		ConfNodeProperty confNodeProperty = new ConfNodeProperty();
		confNodeProperty.setType(GraphType.SEGMENT_MONITOR_DETAIL_IT_ROAD.getType());
		confNodeProperty.setGraphId(segmentPositionVo.getSystemId().toString());
		confNodeProperty.setNodeId(segmentPositionVo.getSegmentId().toString());
		confNodeProperty.setLocationX(Double.valueOf(segmentPositionVo.getLocationX()));
		confNodeProperty.setLocationY(Double.valueOf(segmentPositionVo.getLocationY()));
		return remoteNodePropertyService.setNodePosition(confNodeProperty);
	}

	@Override
	public List<NetworkElementTrend> queryNetworkTrend(SegmentOverviewParams params) {
		List<String> timeList = YDateUtil.getTimeListByJDK8(params.getStartTime(), params.getEndTime(), YDateUtil.HH_MM, 10);
		List<NetworkElementTrend> networkElementTrends = segmentOverviewMapper.queryNetworkTrend(params);
		Map<String, NetworkElementTrend> collectMap = networkElementTrends.stream().collect(Collectors.toMap(NetworkElementTrend::getLabelTime, v -> v, (k1, k2) -> k1));
		List<NetworkElementTrend> list = new ArrayList<>();
		for (String time : timeList) {
			NetworkElementTrend elementTrend =	collectMap.get(time);
			if(ObjectUtil.isEmpty(elementTrend)){
				NetworkElementTrend trend = new NetworkElementTrend();
				trend.setLabelTime(time);
				trend.setCpuRate(0);
				trend.setDiskRate(0);
				trend.setMemoryRate(0);
				list.add(trend);
			}else {
				list.add(elementTrend);
			}
		}
		return list;
	}
}
